( function () {
/**
 * Export draco compressed files from threejs geometry objects.
 *
 * Draco files are compressed and usually are smaller than conventional 3D file formats.
 *
 * The exporter receives a options object containing
 *  - decodeSpeed, indicates how to tune the encoder regarding decode speed (0 gives better speed but worst quality)
 *  - encodeSpeed, indicates how to tune the encoder parameters (0 gives better speed but worst quality)
 *  - encoderMethod
 *  - quantization, indicates the presision of each type of data stored in the draco file in the order (POSITION, NORMAL, COLOR, TEX_COORD, GENERIC)
 *  - exportUvs
 *  - exportNormals
 */

/* global DracoEncoderModule */
class DRACOExporter {
  parse(object, options = {
    decodeSpeed: 5,
    encodeSpeed: 5,
    encoderMethod: DRACOExporter.MESH_EDGEBREAKER_ENCODING,
    quantization: [16, 8, 8, 8, 8],
    exportUvs: true,
    exportNormals: true,
    exportColor: false
  }) {
    if (object.isBufferGeometry === true) {
      throw new Error('DRACOExporter: The first parameter of parse() is now an instance of Mesh or Points.');
    }

    if (DracoEncoderModule === undefined) {
      throw new Error('THREE.DRACOExporter: required the draco_decoder to work.');
    }

    const geometry = object.geometry;
    const dracoEncoder = DracoEncoderModule();
    const encoder = new dracoEncoder.Encoder();
    let builder;
    let dracoObject;

    if (geometry.isBufferGeometry !== true) {
      throw new Error('THREE.DRACOExporter.parse(geometry, options): geometry is not a THREE.BufferGeometry instance.');
    }

    if (object.isMesh === true) {
      builder = new dracoEncoder.MeshBuilder();
      dracoObject = new dracoEncoder.Mesh();
      const vertices = geometry.getAttribute('position');
      builder.AddFloatAttributeToMesh(dracoObject, dracoEncoder.POSITION, vertices.count, vertices.itemSize, vertices.array);
      const faces = geometry.getIndex();

      if (faces !== null) {
        builder.AddFacesToMesh(dracoObject, faces.count / 3, faces.array);
      } else {
        const faces = new (vertices.count > 65535 ? Uint32Array : Uint16Array)(vertices.count);

        for (let i = 0; i < faces.length; i++) {
          faces[i] = i;
        }

        builder.AddFacesToMesh(dracoObject, vertices.count, faces);
      }

      if (options.exportNormals === true) {
        const normals = geometry.getAttribute('normal');

        if (normals !== undefined) {
          builder.AddFloatAttributeToMesh(dracoObject, dracoEncoder.NORMAL, normals.count, normals.itemSize, normals.array);
        }
      }

      if (options.exportUvs === true) {
        const uvs = geometry.getAttribute('uv');

        if (uvs !== undefined) {
          builder.AddFloatAttributeToMesh(dracoObject, dracoEncoder.TEX_COORD, uvs.count, uvs.itemSize, uvs.array);
        }
      }

      if (options.exportColor === true) {
        const colors = geometry.getAttribute('color');

        if (colors !== undefined) {
          builder.AddFloatAttributeToMesh(dracoObject, dracoEncoder.COLOR, colors.count, colors.itemSize, colors.array);
        }
      }
    } else if (object.isPoints === true) {
      builder = new dracoEncoder.PointCloudBuilder();
      dracoObject = new dracoEncoder.PointCloud();
      const vertices = geometry.getAttribute('position');
      builder.AddFloatAttribute(dracoObject, dracoEncoder.POSITION, vertices.count, vertices.itemSize, vertices.array);

      if (options.exportColor === true) {
        const colors = geometry.getAttribute('color');

        if (colors !== undefined) {
          builder.AddFloatAttribute(dracoObject, dracoEncoder.COLOR, colors.count, colors.itemSize, colors.array);
        }
      }
    } else {
      throw new Error('DRACOExporter: Unsupported object type.');
    } //Compress using draco encoder


    const encodedData = new dracoEncoder.DracoInt8Array(); //Sets the desired encoding and decoding speed for the given options from 0 (slowest speed, but the best compression) to 10 (fastest, but the worst compression).

    const encodeSpeed = options.encodeSpeed !== undefined ? options.encodeSpeed : 5;
    const decodeSpeed = options.decodeSpeed !== undefined ? options.decodeSpeed : 5;
    encoder.SetSpeedOptions(encodeSpeed, decodeSpeed); // Sets the desired encoding method for a given geometry.

    if (options.encoderMethod !== undefined) {
      encoder.SetEncodingMethod(options.encoderMethod);
    } // Sets the quantization (number of bits used to represent) compression options for a named attribute.
    // The attribute values will be quantized in a box defined by the maximum extent of the attribute values.


    if (options.quantization !== undefined) {
      for (let i = 0; i < 5; i++) {
        if (options.quantization[i] !== undefined) {
          encoder.SetAttributeQuantization(i, options.quantization[i]);
        }
      }
    }

    let length;

    if (object.isMesh === true) {
      length = encoder.EncodeMeshToDracoBuffer(dracoObject, encodedData);
    } else {
      length = encoder.EncodePointCloudToDracoBuffer(dracoObject, true, encodedData);
    }

    dracoEncoder.destroy(dracoObject);

    if (length === 0) {
      throw new Error('THREE.DRACOExporter: Draco encoding failed.');
    } //Copy encoded data to buffer.


    const outputData = new Int8Array(new ArrayBuffer(length));

    for (let i = 0; i < length; i++) {
      outputData[i] = encodedData.GetValue(i);
    }

    dracoEncoder.destroy(encodedData);
    dracoEncoder.destroy(encoder);
    dracoEncoder.destroy(builder);
    return outputData;
  }

} // Encoder methods


DRACOExporter.MESH_EDGEBREAKER_ENCODING = 1;
DRACOExporter.MESH_SEQUENTIAL_ENCODING = 0; // Geometry type

DRACOExporter.POINT_CLOUD = 0;
DRACOExporter.TRIANGULAR_MESH = 1; // Attribute type

DRACOExporter.INVALID = -1;
DRACOExporter.POSITION = 0;
DRACOExporter.NORMAL = 1;
DRACOExporter.COLOR = 2;
DRACOExporter.TEX_COORD = 3;
DRACOExporter.GENERIC = 4;

THREE.DRACOExporter = DRACOExporter;
} )();
